home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkOption.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-02  |  37.4 KB  |  1,380 lines

  1. /* 
  2.  * tkOption.c --
  3.  *
  4.  *    This module contains procedures to manage the option
  5.  *    database, which allows various strings to be associated
  6.  *    with windows either by name or by class or both.
  7.  *
  8.  * Copyright (c) 1990-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkOption.c 1.54 96/02/27 15:41:25
  15.  */
  16.  
  17. #include "tkPort.h"
  18. #include "tkInt.h"
  19.  
  20. /*
  21.  * The option database is stored as one tree for each main window.
  22.  * Each name or class field in an option is associated with a node or
  23.  * leaf of the tree.  For example, the options "x.y.z" and "x.y*a"
  24.  * each correspond to three nodes in the tree;  they share the nodes
  25.  * "x" and "x.y", but have different leaf nodes.  One of the following
  26.  * structures exists for each node or leaf in the option tree.  It is
  27.  * actually stored as part of the parent node, and describes a particular
  28.  * child of the parent.
  29.  */
  30.  
  31. typedef struct Element {
  32.     Tk_Uid nameUid;            /* Name or class from one element of
  33.                      * an option spec. */
  34.     union {
  35.     struct ElArray *arrayPtr;    /* If this is an intermediate node,
  36.                      * a pointer to a structure describing
  37.                      * the remaining elements of all
  38.                      * options whose prefixes are the
  39.                      * same up through this element. */
  40.     Tk_Uid valueUid;        /* For leaf nodes, this is the string
  41.                      * value of the option. */
  42.     } child;
  43.     int priority;            /* Used to select among matching
  44.                      * options.  Includes both the
  45.                      * priority level and a serial #.
  46.                      * Greater value means higher
  47.                      * priority.  Irrelevant except in
  48.                      * leaf nodes. */
  49.     int flags;                /* OR-ed combination of bits.  See
  50.                      * below for values. */
  51. } Element;
  52.  
  53. /*
  54.  * Flags in Element structures:
  55.  *
  56.  * CLASS -        Non-zero means this element refers to a class,
  57.  *            Zero means this element refers to a name.
  58.  * NODE -        Zero means this is a leaf element (the child
  59.  *            field is a value, not a pointer to another node).
  60.  *            One means this is a node element.
  61.  * WILDCARD -        Non-zero means this there was a star in the
  62.  *            original specification just before this element.
  63.  *            Zero means there was a dot.
  64.  */
  65.  
  66. #define TYPE_MASK        0x7
  67.  
  68. #define CLASS            0x1
  69. #define NODE            0x2
  70. #define WILDCARD        0x4
  71.  
  72. #define EXACT_LEAF_NAME        0x0
  73. #define EXACT_LEAF_CLASS    0x1
  74. #define EXACT_NODE_NAME        0x2
  75. #define EXACT_NODE_CLASS    0x3
  76. #define WILDCARD_LEAF_NAME    0x4
  77. #define WILDCARD_LEAF_CLASS    0x5
  78. #define WILDCARD_NODE_NAME    0x6
  79. #define WILDCARD_NODE_CLASS    0x7
  80.  
  81. /*
  82.  * The following structure is used to manage a dynamic array of
  83.  * Elements.  These structures are used for two purposes:  to store
  84.  * the contents of a node in the option tree, and for the option
  85.  * stacks described below.
  86.  */
  87.  
  88. typedef struct ElArray {
  89.     int arraySize;        /* Number of elements actually
  90.                  * allocated in the "els" array. */
  91.     int numUsed;        /* Number of elements currently in
  92.                  * use out of els. */
  93.     Element *nextToUse;        /* Pointer to &els[numUsed]. */
  94.     Element els[1];        /* Array of structures describing
  95.                  * children of this node.  The
  96.                  * array will actually contain enough
  97.                  * elements for all of the children
  98.                  * (and even a few extras, perhaps).
  99.                  * This must be the last field in
  100.                  * the structure. */
  101. } ElArray;
  102.  
  103. #define EL_ARRAY_SIZE(numEls) ((unsigned) (sizeof(ElArray) \
  104.     + ((numEls)-1)*sizeof(Element)))
  105. #define INITIAL_SIZE 5
  106.  
  107. /*
  108.  * In addition to the option tree, which is a relatively static structure,
  109.  * there are eight additional structures called "stacks", which are used
  110.  * to speed up queries into the option database.  The stack structures
  111.  * are designed for the situation where an individual widget makes repeated
  112.  * requests for its particular options.  The requests differ only in
  113.  * their last name/class, so during the first request we extract all
  114.  * the options pertaining to the particular widget and save them in a
  115.  * stack-like cache;  subsequent requests for the same widget can search
  116.  * the cache relatively quickly.  In fact, the cache is a hierarchical
  117.  * one, storing a list of relevant options for this widget and all of
  118.  * its ancestors up to the application root;  hence the name "stack".
  119.  *
  120.  * Each of the eight stacks consists of an array of Elements, ordered in
  121.  * terms of levels in the window hierarchy.  All the elements relevant
  122.  * for the top-level widget appear first in the array, followed by all
  123.  * those from the next-level widget on the path to the current widget,
  124.  * etc. down to those for the current widget.
  125.  *
  126.  * Cached information is divided into eight stacks according to the
  127.  * CLASS, NODE, and WILDCARD flags.  Leaf and non-leaf information is
  128.  * kept separate to speed up individual probes (non-leaf information is
  129.  * only relevant when building the stacks, but isn't relevant when
  130.  * making probes;  similarly, only non-leaf information is relevant
  131.  * when the stacks are being extended to the next widget down in the
  132.  * widget hierarchy).  Wildcard elements are handled separately from
  133.  * "exact" elements because once they appear at a particular level in
  134.  * the stack they remain active for all deeper levels;  exact elements
  135.  * are only relevant at a particular level.  For example, when searching
  136.  * for options relevant in a particular window, the entire wildcard
  137.  * stacks get checked, but only the portions of the exact stacks that
  138.  * pertain to the window's parent.  Lastly, name and class stacks are
  139.  * kept separate because different search keys are used when searching
  140.  * them;  keeping them separate speeds up the searches.
  141.  */
  142.  
  143. #define NUM_STACKS 8
  144. static ElArray *stacks[NUM_STACKS];
  145. static TkWindow *cachedWindow = NULL;    /* Lowest-level window currently
  146.                      * loaded in stacks at present. 
  147.                      * NULL means stacks have never
  148.                      * been used, or have been
  149.                      * invalidated because of a change
  150.                      * to the database. */
  151.  
  152. /*
  153.  * One of the following structures is used to keep track of each
  154.  * level in the stacks.
  155.  */
  156.  
  157. typedef struct StackLevel {
  158.     TkWindow *winPtr;        /* Window corresponding to this stack
  159.                  * level. */
  160.     int bases[NUM_STACKS];    /* For each stack, index of first
  161.                  * element on stack corresponding to
  162.                  * this level (used to restore "numUsed"
  163.                  * fields when popping out of a level. */
  164. } StackLevel;
  165.  
  166. /*
  167.  * Information about all of the stack levels that are currently
  168.  * active.  This array grows dynamically to become as large as needed.
  169.  */
  170.  
  171. static StackLevel *levels = NULL;
  172.                 /* Array describing current stack. */
  173. static int numLevels = 0;    /* Total space allocated. */
  174. static int curLevel = -1;    /* Highest level currently in use.  Note:
  175.                  * curLevel is never 0!  (I don't remember
  176.                  * why anymore...) */
  177.  
  178. /*
  179.  * The variable below is a serial number for all options entered into
  180.  * the database so far.  It increments on each addition to the option
  181.  * database.  It is used in computing option priorities, so that the
  182.  * most recent entry wins when choosing between options at the same
  183.  * priority level.
  184.  */
  185.  
  186. static int serial = 0;
  187.  
  188. /*
  189.  * Special "no match" Element to use as default for searches.
  190.  */
  191.  
  192. static Element defaultMatch;
  193.  
  194. /*
  195.  * Forward declarations for procedures defined in this file:
  196.  */
  197.  
  198. static int        AddFromString _ANSI_ARGS_((Tcl_Interp *interp,
  199.                 Tk_Window tkwin, char *string, int priority));
  200. static void        ClearOptionTree _ANSI_ARGS_((ElArray *arrayPtr));
  201. static ElArray *    ExtendArray _ANSI_ARGS_((ElArray *arrayPtr,
  202.                 Element *elPtr));
  203. static void        ExtendStacks _ANSI_ARGS_((ElArray *arrayPtr,
  204.                 int leaf));
  205. static int        GetDefaultOptions _ANSI_ARGS_((Tcl_Interp *interp,
  206.                 TkWindow *winPtr));    
  207. static ElArray *    NewArray _ANSI_ARGS_((int numEls));    
  208. static void        OptionInit _ANSI_ARGS_((TkMainInfo *mainPtr));
  209. static int        ParsePriority _ANSI_ARGS_((Tcl_Interp *interp,
  210.                 char *string));
  211. static int        ReadOptionFile _ANSI_ARGS_((Tcl_Interp *interp,
  212.                 Tk_Window tkwin, char *fileName, int priority));
  213. static void        SetupStacks _ANSI_ARGS_((TkWindow *winPtr, int leaf));
  214.  
  215. /*
  216.  *--------------------------------------------------------------
  217.  *
  218.  * Tk_AddOption --
  219.  *
  220.  *    Add a new option to the option database.
  221.  *
  222.  * Results:
  223.  *    None.
  224.  *
  225.  * Side effects:
  226.  *    Information is added to the option database.
  227.  *
  228.  *--------------------------------------------------------------
  229.  */
  230.  
  231. void
  232. Tk_AddOption(tkwin, name, value, priority)
  233.     Tk_Window tkwin;        /* Window token;  option will be associated
  234.                  * with main window for this window. */
  235.     char *name;            /* Multi-element name of option. */
  236.     char *value;        /* String value for option. */
  237.     int priority;        /* Overall priority level to use for
  238.                  * this option, such as TK_USER_DEFAULT_PRIO
  239.                  * or TK_INTERACTIVE_PRIO.  Must be between
  240.                  * 0 and TK_MAX_PRIO. */
  241. {
  242.     TkWindow *winPtr = ((TkWindow *) tkwin)->mainPtr->winPtr;
  243.     register ElArray **arrayPtrPtr;
  244.     register Element *elPtr;
  245.     Element newEl;
  246.     register char *p;
  247.     char *field;
  248.     int count, firstField, length;
  249. #define TMP_SIZE 100
  250.     char tmp[TMP_SIZE+1];
  251.  
  252.     if (winPtr->mainPtr->optionRootPtr == NULL) {
  253.     OptionInit(winPtr->mainPtr);
  254.     }
  255.     cachedWindow = NULL;    /* Invalidate the cache. */
  256.  
  257.     /*
  258.      * Compute the priority for the new element, including both the
  259.      * overall level and the serial number (to disambiguate with the
  260.      * level).
  261.      */
  262.  
  263.     if (priority < 0) {
  264.     priority = 0;
  265.     } else if (priority > TK_MAX_PRIO) {
  266.     priority = TK_MAX_PRIO;
  267.     }
  268.     newEl.priority = (priority << 24) + serial;
  269.     serial++;
  270.  
  271.     /*
  272.      * Parse the option one field at a time.
  273.      */
  274.  
  275.     arrayPtrPtr = &(((TkWindow *) tkwin)->mainPtr->optionRootPtr);
  276.     p = name;
  277.     for (firstField = 1; ; firstField = 0) {
  278.  
  279.     /*
  280.      * Scan the next field from the name and convert it to a Tk_Uid.
  281.      * Must copy the field before calling Tk_Uid, so that a terminating
  282.      * NULL may be added without modifying the source string.
  283.      */
  284.  
  285.     if (*p == '*') {
  286.         newEl.flags = WILDCARD;
  287.         p++;
  288.     } else {
  289.         newEl.flags = 0;
  290.     }
  291.     field = p;
  292.     while ((*p != 0) && (*p != '.') && (*p != '*')) {
  293.         p++;
  294.     }
  295.     length = p - field;
  296.     if (length > TMP_SIZE) {
  297.         length = TMP_SIZE;
  298.     }
  299.     strncpy(tmp, field, (size_t) length);
  300.     tmp[length] = 0;
  301.     newEl.nameUid = Tk_GetUid(tmp);
  302.     if (isupper(UCHAR(*field))) {
  303.         newEl.flags |= CLASS;
  304.     }
  305.  
  306.     if (*p != 0) {
  307.  
  308.         /*
  309.          * New element will be a node.  If this option can't possibly
  310.          * apply to this main window, then just skip it.  Otherwise,
  311.          * add it to the parent, if it isn't already there, and descend
  312.          * into it.
  313.          */
  314.  
  315.         newEl.flags |= NODE;
  316.         if (firstField && !(newEl.flags & WILDCARD)
  317.             && (newEl.nameUid != winPtr->nameUid)
  318.             && (newEl.nameUid != winPtr->classUid)) {
  319.         return;
  320.         }
  321.         for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed;
  322.             ; elPtr++, count--) {
  323.         if (count == 0) {
  324.             newEl.child.arrayPtr = NewArray(5);
  325.             *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl);
  326.             arrayPtrPtr = &((*arrayPtrPtr)->nextToUse[-1].child.arrayPtr);
  327.             break;
  328.         }
  329.         if ((elPtr->nameUid == newEl.nameUid)
  330.             && (elPtr->flags == newEl.flags)) {
  331.             arrayPtrPtr = &(elPtr->child.arrayPtr);
  332.             break;
  333.         }
  334.         }
  335.         if (*p == '.') {
  336.         p++;
  337.         }
  338.     } else {
  339.  
  340.         /*
  341.          * New element is a leaf.  Add it to the parent, if it isn't
  342.          * already there.  If it exists already, keep whichever value
  343.          * has highest priority.
  344.          */
  345.  
  346.         newEl.child.valueUid = Tk_GetUid(value);
  347.         for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed;
  348.             ; elPtr++, count--) {
  349.         if (count == 0) {
  350.             *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl);
  351.             return;
  352.         }
  353.         if ((elPtr->nameUid == newEl.nameUid)
  354.             && (elPtr->flags == newEl.flags)) {
  355.             if (elPtr->priority < newEl.priority) {
  356.             elPtr->priority = newEl.priority;
  357.             elPtr->child.valueUid = newEl.child.valueUid;
  358.             }
  359.             return;
  360.         }
  361.         }
  362.     }
  363.     }
  364. }
  365.  
  366. /*
  367.  *--------------------------------------------------------------
  368.  *
  369.  * Tk_GetOption --
  370.  *
  371.  *    Retrieve an option from the option database.
  372.  *
  373.  * Results:
  374.  *    The return value is the value specified in the option
  375.  *    database for the given name and class on the given
  376.  *    window.  If there is nothing specified in the database
  377.  *    for that option, then NULL is returned.
  378.  *
  379.  * Side effects:
  380.  *    The internal caches used to speed up option mapping
  381.  *    may be modified, if this tkwin is different from the
  382.  *    last tkwin used for option retrieval.
  383.  *
  384.  *--------------------------------------------------------------
  385.  */
  386.  
  387. Tk_Uid
  388. Tk_GetOption(tkwin, name, className)
  389.     Tk_Window tkwin;        /* Token for window that option is
  390.                  * associated with. */
  391.     char *name;            /* Name of option. */
  392.     char *className;        /* Class of option.  NULL means there
  393.                  * is no class for this option:  just
  394.                  * check for name. */
  395. {
  396.     Tk_Uid nameId, classId;
  397.     register Element *elPtr, *bestPtr;
  398.     register int count;
  399.  
  400.     /*
  401.      * Note:  no need to call OptionInit here:  it will be done by
  402.      * the SetupStacks call below (squeeze out those nanoseconds).
  403.      */
  404.  
  405.     if (tkwin != (Tk_Window) cachedWindow) {
  406.     SetupStacks((TkWindow *) tkwin, 1);
  407.     }
  408.  
  409.     nameId = Tk_GetUid(name);
  410.     bestPtr = &defaultMatch;
  411.     for (elPtr = stacks[EXACT_LEAF_NAME]->els,
  412.         count = stacks[EXACT_LEAF_NAME]->numUsed; count > 0;
  413.         elPtr++, count--) {
  414.     if ((elPtr->nameUid == nameId)
  415.         && (elPtr->priority > bestPtr->priority)) {
  416.         bestPtr = elPtr;
  417.     }
  418.     }
  419.     for (elPtr = stacks[WILDCARD_LEAF_NAME]->els,
  420.         count = stacks[WILDCARD_LEAF_NAME]->numUsed; count > 0;
  421.         elPtr++, count--) {
  422.     if ((elPtr->nameUid == nameId)
  423.         && (elPtr->priority > bestPtr->priority)) {
  424.         bestPtr = elPtr;
  425.     }
  426.     }
  427.     if (className != NULL) {
  428.     classId = Tk_GetUid(className);
  429.     for (elPtr = stacks[EXACT_LEAF_CLASS]->els,
  430.         count = stacks[EXACT_LEAF_CLASS]->numUsed; count > 0;
  431.         elPtr++, count--) {
  432.         if ((elPtr->nameUid == classId)
  433.             && (elPtr->priority > bestPtr->priority)) {
  434.         bestPtr = elPtr;
  435.         }
  436.     }
  437.     for (elPtr = stacks[WILDCARD_LEAF_CLASS]->els,
  438.         count = stacks[WILDCARD_LEAF_CLASS]->numUsed; count > 0;
  439.         elPtr++, count--) {
  440.         if ((elPtr->nameUid == classId)
  441.             && (elPtr->priority > bestPtr->priority)) {
  442.         bestPtr = elPtr;
  443.         }
  444.     }
  445.     }
  446.     return bestPtr->child.valueUid;
  447. }
  448.  
  449. /*
  450.  *--------------------------------------------------------------
  451.  *
  452.  * Tk_OptionCmd --
  453.  *
  454.  *    This procedure is invoked to process the "option" Tcl command.
  455.  *    See the user documentation for details on what it does.
  456.  *
  457.  * Results:
  458.  *    A standard Tcl result.
  459.  *
  460.  * Side effects:
  461.  *    See the user documentation.
  462.  *
  463.  *--------------------------------------------------------------
  464.  */
  465.  
  466. int
  467. Tk_OptionCmd(clientData, interp, argc, argv)
  468.     ClientData clientData;    /* Main window associated with
  469.                  * interpreter. */
  470.     Tcl_Interp *interp;        /* Current interpreter. */
  471.     int argc;            /* Number of arguments. */
  472.     char **argv;        /* Argument strings. */
  473. {
  474.     Tk_Window tkwin = (Tk_Window) clientData;
  475.     size_t length;
  476.     char c;
  477.  
  478.     if (argc < 2) {
  479.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  480.         " cmd arg ?arg ...?\"", (char *) NULL);
  481.     return TCL_ERROR;
  482.     }
  483.     c = argv[1][0];
  484.     length = strlen(argv[1]);
  485.     if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)) {
  486.     int priority;
  487.  
  488.     if ((argc != 4) && (argc != 5)) {
  489.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  490.             argv[0], " add pattern value ?priority?\"", (char *) NULL);
  491.         return TCL_ERROR;
  492.     }
  493.     if (argc == 4) {
  494.         priority = TK_INTERACTIVE_PRIO;
  495.     } else {
  496.         priority = ParsePriority(interp, argv[4]);
  497.         if (priority < 0) {
  498.         return TCL_ERROR;
  499.         }
  500.     }
  501.     Tk_AddOption(tkwin, argv[2], argv[3], priority);
  502.     return TCL_OK;
  503.     } else if ((c == 'c') && (strncmp(argv[1], "clear", length) == 0)) {
  504.     TkMainInfo *mainPtr;
  505.  
  506.     if (argc != 2) {
  507.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  508.             argv[0], " clear\"", (char *) NULL);
  509.         return TCL_ERROR;
  510.     }
  511.     mainPtr = ((TkWindow *) tkwin)->mainPtr;
  512.     if (mainPtr->optionRootPtr != NULL) {
  513.         ClearOptionTree(mainPtr->optionRootPtr);
  514.         mainPtr->optionRootPtr = NULL;
  515.     }
  516.     cachedWindow = NULL;
  517.     return TCL_OK;
  518.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  519.     Tk_Window window;
  520.     Tk_Uid value;
  521.  
  522.     if (argc != 5) {
  523.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  524.             argv[0], " get window name class\"", (char *) NULL);
  525.         return TCL_ERROR;
  526.     }
  527.     window = Tk_NameToWindow(interp, argv[2], tkwin);
  528.     if (window == NULL) {
  529.         return TCL_ERROR;
  530.     }
  531.     value = Tk_GetOption(window, argv[3], argv[4]);
  532.     if (value != NULL) {
  533. #ifdef STk_CODE
  534.         STk_stringify_result(interp, value);
  535. #else
  536.         interp->result = value;
  537. #endif
  538.     }
  539.     return TCL_OK;
  540.     } else if ((c == 'r') && (strncmp(argv[1], "readfile", length) == 0)) {
  541.     int priority;
  542.  
  543.     if ((argc != 3) && (argc != 4)) {
  544.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  545.             argv[0], " readfile fileName ?priority?\"",
  546.             (char *) NULL);
  547.         return TCL_ERROR;
  548.     }
  549.     if (argc == 4) {
  550.         priority = ParsePriority(interp, argv[3]);
  551.         if (priority < 0) {
  552.         return TCL_ERROR;
  553.         }
  554.     } else {
  555.         priority = TK_INTERACTIVE_PRIO;
  556.     }
  557.     return ReadOptionFile(interp, tkwin, argv[2], priority);
  558.     } else {
  559.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  560.         "\": must be add, clear, get, or readfile", (char *) NULL);
  561.     return TCL_ERROR;
  562.     }
  563. }
  564.  
  565. /*
  566.  *--------------------------------------------------------------
  567.  *
  568.  * TkOptionDeadWindow --
  569.  *
  570.  *    This procedure is called whenever a window is deleted.
  571.  *    It cleans up any option-related stuff associated with
  572.  *    the window.
  573.  *
  574.  * Results:
  575.  *    None.
  576.  *
  577.  * Side effects:
  578.  *    Option-related resources are freed.  See code below
  579.  *    for details.
  580.  *
  581.  *--------------------------------------------------------------
  582.  */
  583.  
  584. void
  585. TkOptionDeadWindow(winPtr)
  586.     register TkWindow *winPtr;        /* Window to be cleaned up. */
  587. {
  588.     /*
  589.      * If this window is in the option stacks, then clear the stacks.
  590.      */
  591.  
  592.     if (winPtr->optionLevel != -1) {
  593.     int i;
  594.  
  595.     for (i = 1; i <= curLevel; i++) {
  596.         levels[i].winPtr->optionLevel = -1;
  597.     }
  598.     curLevel = -1;
  599.     cachedWindow = NULL;
  600.     }
  601.  
  602.     /*
  603.      * If this window was a main window, then delete its option
  604.      * database.
  605.      */
  606.  
  607.     if ((winPtr->mainPtr->winPtr == winPtr)
  608.         && (winPtr->mainPtr->optionRootPtr != NULL)) {
  609.     ClearOptionTree(winPtr->mainPtr->optionRootPtr);
  610.     winPtr->mainPtr->optionRootPtr = NULL;
  611.     }
  612. }
  613.  
  614. /*
  615.  *----------------------------------------------------------------------
  616.  *
  617.  * TkOptionClassChanged --
  618.  *
  619.  *    This procedure is invoked when a window's class changes.  If
  620.  *    the window is on the option cache, this procedure flushes
  621.  *    any information for the window, since the new class could change
  622.  *    what is relevant.
  623.  *
  624.  * Results:
  625.  *    None.
  626.  *
  627.  * Side effects:
  628.  *    The option cache may be flushed in part or in whole.
  629.  *
  630.  *----------------------------------------------------------------------
  631.  */
  632.  
  633. void
  634. TkOptionClassChanged(winPtr)
  635.     TkWindow *winPtr;            /* Window whose class changed. */
  636. {
  637.     int i, j, *basePtr;
  638.     ElArray *arrayPtr;
  639.  
  640.     if (winPtr->optionLevel == -1) {
  641.     return;
  642.     }
  643.  
  644.     /*
  645.      * Find the lowest stack level that refers to this window, then
  646.      * flush all of the levels above the matching one.
  647.      */
  648.  
  649.     for (i = 1; i <= curLevel; i++) {
  650.     if (levels[i].winPtr == winPtr) {
  651.         for (j = i; j <= curLevel; j++) {
  652.         levels[j].winPtr->optionLevel = -1;
  653.         }
  654.         curLevel = i-1;
  655.         basePtr = levels[i].bases;
  656.         for (j = 0; j < NUM_STACKS; j++) {
  657.         arrayPtr = stacks[j];
  658.         arrayPtr->numUsed = basePtr[j];
  659.         arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed];
  660.         }
  661.         if (curLevel <= 0) {
  662.         cachedWindow = NULL;
  663.         } else {
  664.         cachedWindow = levels[curLevel].winPtr;
  665.         }
  666.         break;
  667.     }
  668.     }
  669. }
  670.  
  671. /*
  672.  *----------------------------------------------------------------------
  673.  *
  674.  * ParsePriority --
  675.  *
  676.  *    Parse a string priority value.
  677.  *
  678.  * Results:
  679.  *    The return value is the integer priority level corresponding
  680.  *    to string, or -1 if string doesn't point to a valid priority level.
  681.  *    In this case, an error message is left in interp->result.
  682.  *
  683.  * Side effects:
  684.  *    None.
  685.  *
  686.  *----------------------------------------------------------------------
  687.  */
  688.  
  689. static int
  690. ParsePriority(interp, string)
  691.     Tcl_Interp *interp;        /* Interpreter to use for error reporting. */
  692.     char *string;        /* Describes a priority level, either
  693.                  * symbolically or numerically. */
  694. {
  695.     int priority, c;
  696.     size_t length;
  697.  
  698.     c = string[0];
  699.     length = strlen(string);
  700.     if ((c == 'w')
  701.         && (strncmp(string, "widgetDefault", length) == 0)) {
  702.     return TK_WIDGET_DEFAULT_PRIO;
  703.     } else if ((c == 's')
  704.         && (strncmp(string, "startupFile", length) == 0)) {
  705.     return TK_STARTUP_FILE_PRIO;
  706.     } else if ((c == 'u')
  707.         && (strncmp(string, "userDefault", length) == 0)) {
  708.     return TK_USER_DEFAULT_PRIO;
  709.     } else if ((c == 'i')
  710.         && (strncmp(string, "interactive", length) == 0)) {
  711.     return TK_INTERACTIVE_PRIO;
  712.     } else {
  713.     char *end;
  714.  
  715.     priority = strtoul(string, &end, 0);
  716.     if ((end == string) || (*end != 0) || (priority < 0)
  717.         || (priority > 100)) {
  718.         Tcl_AppendResult(interp,  "bad priority level \"", string,
  719.             "\": must be widgetDefault, startupFile, userDefault, ",
  720.             "interactive, or a number between 0 and 100",
  721.             (char *) NULL);
  722.         return -1;
  723.     }
  724.     }
  725.     return priority;
  726. }
  727.  
  728. /*
  729.  *----------------------------------------------------------------------
  730.  *
  731.  * AddFromString --
  732.  *
  733.  *    Given a string containing lines in the standard format for
  734.  *    X resources (see other documentation for details on what this
  735.  *    is), parse the resource specifications and enter them as options
  736.  *    for tkwin's main window.
  737.  *
  738.  * Results:
  739.  *    The return value is a standard Tcl return code.  In the case of
  740.  *    an error in parsing string, TCL_ERROR will be returned and an
  741.  *    error message will be left in interp->result.  The memory at
  742.  *    string is totally trashed by this procedure.  If you care about
  743.  *    its contents, make a copy before calling here.
  744.  *
  745.  * Side effects:
  746.  *    None.
  747.  *
  748.  *----------------------------------------------------------------------
  749.  */
  750.  
  751. static int
  752. AddFromString(interp, tkwin, string, priority)
  753.     Tcl_Interp *interp;        /* Interpreter to use for reporting results. */
  754.     Tk_Window tkwin;        /* Token for window:  options are entered
  755.                  * for this window's main window. */
  756.     char *string;        /* String containing option specifiers. */
  757.     int priority;        /* Priority level to use for options in
  758.                  * this string, such as TK_USER_DEFAULT_PRIO
  759.                  * or TK_INTERACTIVE_PRIO.  Must be between
  760.                  * 0 and TK_MAX_PRIO. */
  761. {
  762.     register char *src, *dst;
  763.     char *name, *value;
  764.     int lineNum;
  765.  
  766.     src = string;
  767.     lineNum = 1;
  768.     while (1) {
  769.  
  770.     /*
  771.      * Skip leading white space and empty lines and comment lines, and
  772.      * check for the end of the spec.
  773.      */
  774.  
  775.     while ((*src == ' ') || (*src == '\t')) {
  776.         src++;
  777.     }
  778.     if ((*src == '#') || (*src == '!')) {
  779.         do {
  780.         src++;
  781.         if ((src[0] == '\\') && (src[1] == '\n')) {
  782.             src += 2;
  783.             lineNum++;
  784.         }
  785.         } while ((*src != '\n') && (*src != 0));
  786.     }
  787.     if (*src == '\n') {
  788.         src++;
  789.         lineNum++;
  790.         continue;
  791.     } 
  792.     if (*src == '\0') {
  793.         break;
  794.     }
  795.  
  796.     /*
  797.      * Parse off the option name, collapsing out backslash-newline
  798.      * sequences of course.
  799.      */
  800.  
  801.     dst = name = src;
  802.     while (*src != ':') {
  803.         if ((*src == '\0') || (*src == '\n')) {
  804.         sprintf(interp->result, "missing colon on line %d",
  805.             lineNum);
  806.         return TCL_ERROR;
  807.         }
  808.         if ((src[0] == '\\') && (src[1] == '\n')) {
  809.         src += 2;
  810.         lineNum++;
  811.         } else {
  812.         *dst = *src;
  813.         dst++;
  814.         src++;
  815.         }
  816.     }
  817.  
  818.     /*
  819.      * Eliminate trailing white space on the name, and null-terminate
  820.      * it.
  821.      */
  822.  
  823.     while ((dst != name) && ((dst[-1] == ' ') || (dst[-1] == '\t'))) {
  824.         dst--;
  825.     }
  826.     *dst = '\0';
  827.  
  828.     /*
  829.      * Skip white space between the name and the value.
  830.      */
  831.  
  832.     src++;
  833.     while ((*src == ' ') || (*src == '\t')) {
  834.         src++;
  835.     }
  836.     if (*src == '\0') {
  837.         sprintf(interp->result, "missing value on line %d", lineNum);
  838.         return TCL_ERROR;
  839.     }
  840.  
  841.     /*
  842.      * Parse off the value, squeezing out backslash-newline sequences
  843.      * along the way.
  844.      */
  845.  
  846.     dst = value = src;
  847.     while (*src != '\n') {
  848.         if (*src == '\0') {
  849.         sprintf(interp->result, "missing newline on line %d",
  850.             lineNum);
  851.         return TCL_ERROR;
  852.         }
  853.         if ((src[0] == '\\') && (src[1] == '\n')) {
  854.         src += 2;
  855.         lineNum++;
  856.         } else {
  857.         *dst = *src;
  858.         dst++;
  859.         src++;
  860.         }
  861.     }
  862.     *dst = 0;
  863.  
  864.     /*
  865.      * Enter the option into the database.
  866.      */
  867.  
  868.     Tk_AddOption(tkwin, name, value, priority);
  869.     src++;
  870.     lineNum++;
  871.     }
  872.     return TCL_OK;
  873. }
  874.  
  875. /*
  876.  *----------------------------------------------------------------------
  877.  *
  878.  * ReadOptionFile --
  879.  *
  880.  *     Read a file of options ("resources" in the old X terminology)
  881.  *    and load them into the option database.
  882.  *
  883.  * Results:
  884.  *    The return value is a standard Tcl return code.  In the case of
  885.  *    an error in parsing string, TCL_ERROR will be returned and an
  886.  *    error message will be left in interp->result.
  887.  *
  888.  * Side effects:
  889.  *    None.
  890.  *
  891.  *----------------------------------------------------------------------
  892.  */
  893.  
  894. static int
  895. ReadOptionFile(interp, tkwin, fileName, priority)
  896.     Tcl_Interp *interp;        /* Interpreter to use for reporting results. */
  897.     Tk_Window tkwin;        /* Token for window:  options are entered
  898.                  * for this window's main window. */
  899.     char *fileName;        /* Name of file containing options. */
  900.     int priority;        /* Priority level to use for options in
  901.                  * this file, such as TK_USER_DEFAULT_PRIO
  902.                  * or TK_INTERACTIVE_PRIO.  Must be between
  903.                  * 0 and TK_MAX_PRIO. */
  904. {
  905.     char *realName, *buffer;
  906.     int result, bufferSize;
  907.     Tcl_Channel chan;
  908.     Tcl_DString newName;
  909.  
  910.     realName = Tcl_TranslateFileName(interp, fileName, &newName);
  911.     if (realName == NULL) {
  912.     return TCL_ERROR;
  913.     }
  914.     chan = Tcl_OpenFileChannel(interp, realName, "r", 0);
  915.     Tcl_DStringFree(&newName);
  916.     if (chan == NULL) {
  917.     return TCL_ERROR;
  918.     }
  919.  
  920.     /*
  921.      * Compute size of file by seeking to the end of the file.
  922.      */
  923.     
  924.     bufferSize = Tcl_Seek(chan, 0L, SEEK_END);
  925.     (void) Tcl_Seek(chan, 0L, SEEK_SET);
  926.     
  927.     buffer = (char *) ckalloc((unsigned) bufferSize+1);
  928.     if (Tcl_Read(chan, buffer, bufferSize) != bufferSize) {
  929.     Tcl_AppendResult(interp, "error reading file \"", fileName, "\"",
  930.         (char *) NULL);
  931.     Tcl_Close(NULL, chan);
  932.     return TCL_ERROR;
  933.     }
  934.     Tcl_Close(NULL, chan);
  935.     buffer[bufferSize] = 0;
  936.     result = AddFromString(interp, tkwin, buffer, priority);
  937.     ckfree(buffer);
  938.     return result;
  939. }
  940.  
  941. /*
  942.  *--------------------------------------------------------------
  943.  *
  944.  * NewArray --
  945.  *
  946.  *    Create a new ElArray structure of a given size.
  947.  *
  948.  * Results:
  949.  *    The return value is a pointer to a properly initialized
  950.  *    element array with "numEls" space.  The array is marked
  951.  *    as having no active elements.
  952.  *
  953.  * Side effects:
  954.  *    Memory is allocated.
  955.  *
  956.  *--------------------------------------------------------------
  957.  */
  958.  
  959. static ElArray *
  960. NewArray(numEls)
  961.     int numEls;            /* How many elements of space to allocate. */
  962. {
  963.     register ElArray *arrayPtr;
  964.  
  965.     arrayPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(numEls));
  966.     arrayPtr->arraySize = numEls;
  967.     arrayPtr->numUsed = 0;
  968.     arrayPtr->nextToUse = arrayPtr->els;
  969.     return arrayPtr;
  970. }
  971.  
  972. /*
  973.  *--------------------------------------------------------------
  974.  *
  975.  * ExtendArray --
  976.  *
  977.  *    Add a new element to an array, extending the array if
  978.  *    necessary.
  979.  *
  980.  * Results:
  981.  *    The return value is a pointer to the new array, which
  982.  *    will be different from arrayPtr if the array got expanded.
  983.  *
  984.  * Side effects:
  985.  *    Memory may be allocated or freed.
  986.  *
  987.  *--------------------------------------------------------------
  988.  */
  989.  
  990. static ElArray *
  991. ExtendArray(arrayPtr, elPtr)
  992.     register ElArray *arrayPtr;        /* Array to be extended. */
  993.     register Element *elPtr;        /* Element to be copied into array. */
  994. {
  995.     /*
  996.      * If the current array has filled up, make it bigger.
  997.      */
  998.  
  999.     if (arrayPtr->numUsed >= arrayPtr->arraySize) {
  1000.     register ElArray *newPtr;
  1001.  
  1002.     newPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(2*arrayPtr->arraySize));
  1003.     newPtr->arraySize = 2*arrayPtr->arraySize;
  1004.     newPtr->numUsed = arrayPtr->numUsed;
  1005.     newPtr->nextToUse = &newPtr->els[newPtr->numUsed];
  1006.     memcpy((VOID *) newPtr->els, (VOID *) arrayPtr->els,
  1007.         (arrayPtr->arraySize*sizeof(Element)));
  1008.     ckfree((char *) arrayPtr);
  1009.     arrayPtr = newPtr;
  1010.     }
  1011.  
  1012.     *arrayPtr->nextToUse = *elPtr;
  1013.     arrayPtr->nextToUse++;
  1014.     arrayPtr->numUsed++;
  1015.     return arrayPtr;
  1016. }
  1017.  
  1018. /*
  1019.  *--------------------------------------------------------------
  1020.  *
  1021.  * SetupStacks --
  1022.  *
  1023.  *    Arrange the stacks so that they cache all the option
  1024.  *    information for a particular window.
  1025.  *
  1026.  * Results:
  1027.  *    None.
  1028.  *
  1029.  * Side effects:
  1030.  *    The stacks are modified to hold information for tkwin
  1031.  *    and all its ancestors in the window hierarchy.
  1032.  *
  1033.  *--------------------------------------------------------------
  1034.  */
  1035.  
  1036. static void
  1037. SetupStacks(winPtr, leaf)
  1038.     TkWindow *winPtr;        /* Window for which information is to
  1039.                  * be cached. */
  1040.     int leaf;            /* Non-zero means this is the leaf
  1041.                  * window being probed.  Zero means this
  1042.                  * is an ancestor of the desired leaf. */
  1043. {
  1044.     int level, i, *iPtr;
  1045.     register StackLevel *levelPtr;
  1046.     register ElArray *arrayPtr;
  1047.  
  1048.     /*
  1049.      * The following array defines the order in which the current
  1050.      * stacks are searched to find matching entries to add to the
  1051.      * stacks.  Given the current priority-based scheme, the order
  1052.      * below is no longer relevant;  all that matters is that an
  1053.      * element is on the list *somewhere*.  The ordering is a relic
  1054.      * of the old days when priorities were determined differently.
  1055.      */
  1056.  
  1057.     static int searchOrder[] = {WILDCARD_NODE_CLASS, WILDCARD_NODE_NAME,
  1058.         EXACT_NODE_CLASS, EXACT_NODE_NAME, -1};
  1059.  
  1060.     if (winPtr->mainPtr->optionRootPtr == NULL) {
  1061.     OptionInit(winPtr->mainPtr);
  1062.     }
  1063.  
  1064.     /*
  1065.      * Step 1:  make sure that options are cached for this window's
  1066.      * parent.
  1067.      */
  1068.  
  1069.     if (winPtr->parentPtr != NULL) {
  1070.     level = winPtr->parentPtr->optionLevel;
  1071.     if ((level == -1) || (cachedWindow == NULL)) {
  1072.         SetupStacks(winPtr->parentPtr, 0);
  1073.         level = winPtr->parentPtr->optionLevel;
  1074.     }
  1075.     level++;
  1076.     } else {
  1077.     level = 1;
  1078.     }
  1079.  
  1080.     /*
  1081.      * Step 2:  pop extra unneeded information off the stacks and
  1082.      * mark those windows as no longer having cached information.
  1083.      */
  1084.  
  1085.     if (curLevel >= level) {
  1086.     while (curLevel >= level) {
  1087.         levels[curLevel].winPtr->optionLevel = -1;
  1088.         curLevel--;
  1089.     }
  1090.     levelPtr = &levels[level];
  1091.     for (i = 0; i < NUM_STACKS; i++) {
  1092.         arrayPtr = stacks[i];
  1093.         arrayPtr->numUsed = levelPtr->bases[i];
  1094.         arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed];
  1095.     }
  1096.     }
  1097.     curLevel = winPtr->optionLevel = level;
  1098.  
  1099.     /*
  1100.      * Step 3:  if the root database information isn't loaded or
  1101.      * isn't valid, initialize level 0 of the stack from the
  1102.      * database root (this only happens if winPtr is a main window).
  1103.      */
  1104.  
  1105.     if ((curLevel == 1)
  1106.         && ((cachedWindow == NULL)
  1107.         || (cachedWindow->mainPtr != winPtr->mainPtr))) {
  1108.     for (i = 0; i < NUM_STACKS; i++) {
  1109.         arrayPtr = stacks[i];
  1110.         arrayPtr->numUsed = 0;
  1111.         arrayPtr->nextToUse = arrayPtr->els;
  1112.     }
  1113.     ExtendStacks(winPtr->mainPtr->optionRootPtr, 0);
  1114.     }
  1115.  
  1116.     /*
  1117.      * Step 4: create a new stack level;  grow the level array if
  1118.      * we've run out of levels.  Clear the stacks for EXACT_LEAF_NAME
  1119.      * and EXACT_LEAF_CLASS (anything that was there is of no use
  1120.      * any more).
  1121.      */
  1122.  
  1123.     if (curLevel >= numLevels) {
  1124.     StackLevel *newLevels;
  1125.  
  1126.     newLevels = (StackLevel *) ckalloc((unsigned)
  1127.         (numLevels*2*sizeof(StackLevel)));
  1128.     memcpy((VOID *) newLevels, (VOID *) levels,
  1129.         (numLevels*sizeof(StackLevel)));
  1130.     ckfree((char *) levels);
  1131.     numLevels *= 2;
  1132.     levels = newLevels;
  1133.     }
  1134.     levelPtr = &levels[curLevel];
  1135.     levelPtr->winPtr = winPtr;
  1136.     arrayPtr = stacks[EXACT_LEAF_NAME];
  1137.     arrayPtr->numUsed = 0;
  1138.     arrayPtr->nextToUse = arrayPtr->els;
  1139.     arrayPtr = stacks[EXACT_LEAF_CLASS];
  1140.     arrayPtr->numUsed = 0;
  1141.     arrayPtr->nextToUse = arrayPtr->els;
  1142.     levelPtr->bases[EXACT_LEAF_NAME] = stacks[EXACT_LEAF_NAME]->numUsed;
  1143.     levelPtr->bases[EXACT_LEAF_CLASS] = stacks[EXACT_LEAF_CLASS]->numUsed;
  1144.     levelPtr->bases[EXACT_NODE_NAME] = stacks[EXACT_NODE_NAME]->numUsed;
  1145.     levelPtr->bases[EXACT_NODE_CLASS] = stacks[EXACT_NODE_CLASS]->numUsed;
  1146.     levelPtr->bases[WILDCARD_LEAF_NAME] = stacks[WILDCARD_LEAF_NAME]->numUsed;
  1147.     levelPtr->bases[WILDCARD_LEAF_CLASS] = stacks[WILDCARD_LEAF_CLASS]->numUsed;
  1148.     levelPtr->bases[WILDCARD_NODE_NAME] = stacks[WILDCARD_NODE_NAME]->numUsed;
  1149.     levelPtr->bases[WILDCARD_NODE_CLASS] = stacks[WILDCARD_NODE_CLASS]->numUsed;
  1150.  
  1151.  
  1152.     /*
  1153.      * Step 5: scan the current stack level looking for matches to this
  1154.      * window's name or class;  where found, add new information to the
  1155.      * stacks.
  1156.      */
  1157.  
  1158.     for (iPtr = searchOrder; *iPtr != -1; iPtr++) {
  1159.     register Element *elPtr;
  1160.     int count;
  1161.     Tk_Uid id;
  1162.  
  1163.     i = *iPtr;
  1164.     if (i & CLASS) {
  1165.         id = winPtr->classUid;
  1166.     } else {
  1167.         id = winPtr->nameUid;
  1168.     }
  1169.     elPtr = stacks[i]->els;
  1170.     count = levelPtr->bases[i];
  1171.  
  1172.     /*
  1173.      * For wildcard stacks, check all entries;  for non-wildcard
  1174.      * stacks, only check things that matched in the parent.
  1175.      */
  1176.  
  1177.     if (!(i & WILDCARD)) {
  1178.         elPtr += levelPtr[-1].bases[i];
  1179.         count -= levelPtr[-1].bases[i];
  1180.     }
  1181.     for ( ; count > 0; elPtr++, count--) {
  1182.         if (elPtr->nameUid != id) {
  1183.         continue;
  1184.         }
  1185.         ExtendStacks(elPtr->child.arrayPtr, leaf);
  1186.     }
  1187.     }
  1188.     cachedWindow = winPtr;
  1189. }
  1190.  
  1191. /*
  1192.  *--------------------------------------------------------------
  1193.  *
  1194.  * ExtendStacks --
  1195.  *
  1196.  *    Given an element array, copy all the elements from the
  1197.  *    array onto the system stacks (except for irrelevant leaf
  1198.  *    elements).
  1199.  *
  1200.  * Results:
  1201.  *    None.
  1202.  *
  1203.  * Side effects:
  1204.  *    The option stacks are extended.
  1205.  *
  1206.  *--------------------------------------------------------------
  1207.  */
  1208.  
  1209. static void
  1210. ExtendStacks(arrayPtr, leaf)
  1211.     ElArray *arrayPtr;        /* Array of elements to copy onto stacks. */
  1212.     int leaf;            /* If zero, then don't copy exact leaf
  1213.                  * elements. */
  1214. {
  1215.     register int count;
  1216.     register Element *elPtr;
  1217.  
  1218.     for (elPtr = arrayPtr->els, count = arrayPtr->numUsed;
  1219.         count > 0; elPtr++, count--) {
  1220.     if (!(elPtr->flags & (NODE|WILDCARD)) && !leaf) {
  1221.         continue;
  1222.     }
  1223.     stacks[elPtr->flags] = ExtendArray(stacks[elPtr->flags], elPtr);
  1224.     }
  1225. }
  1226.  
  1227. /*
  1228.  *--------------------------------------------------------------
  1229.  *
  1230.  * OptionInit --
  1231.  *
  1232.  *    Initialize data structures for option handling.
  1233.  *
  1234.  * Results:
  1235.  *    None.
  1236.  *
  1237.  * Side effects:
  1238.  *    Option-related data structures get initialized.
  1239.  *
  1240.  *--------------------------------------------------------------
  1241.  */
  1242.  
  1243. static void
  1244. OptionInit(mainPtr)
  1245.     register TkMainInfo *mainPtr;    /* Top-level information about
  1246.                      * window that isn't initialized
  1247.                      * yet. */
  1248. {
  1249.     int i;
  1250.     Tcl_Interp *interp;
  1251.  
  1252.     /*
  1253.      * First, once-only initialization.
  1254.      */
  1255.  
  1256.     if (numLevels == 0) {
  1257.  
  1258.     numLevels = 5;
  1259.     levels = (StackLevel *) ckalloc((unsigned) (5*sizeof(StackLevel)));
  1260.     for (i = 0; i < NUM_STACKS; i++) {
  1261.         stacks[i] = NewArray(10);
  1262.         levels[0].bases[i] = 0;
  1263.     }
  1264.     
  1265.     defaultMatch.nameUid = NULL;
  1266.     defaultMatch.child.valueUid = NULL;
  1267.     defaultMatch.priority = -1;
  1268.     defaultMatch.flags = 0;
  1269.     }
  1270.  
  1271.     /*
  1272.      * Then, per-main-window initialization.  Create and delete dummy
  1273.      * interpreter for message logging.
  1274.      */
  1275.  
  1276.     mainPtr->optionRootPtr = NewArray(20);
  1277.     interp = Tcl_CreateInterp();
  1278.     (void) GetDefaultOptions(interp, mainPtr->winPtr);
  1279.     Tcl_DeleteInterp(interp);
  1280. }
  1281.  
  1282. /*
  1283.  *--------------------------------------------------------------
  1284.  *
  1285.  * ClearOptionTree --
  1286.  *
  1287.  *    This procedure is called to erase everything in a
  1288.  *    hierarchical option database.
  1289.  *
  1290.  * Results:
  1291.  *    None.
  1292.  *
  1293.  * Side effects:
  1294.  *    All the options associated with arrayPtr are deleted,
  1295.  *    along with all option subtrees.  The space pointed to
  1296.  *    by arrayPtr is freed.
  1297.  *
  1298.  *--------------------------------------------------------------
  1299.  */
  1300.  
  1301. static void
  1302. ClearOptionTree(arrayPtr)
  1303.     ElArray *arrayPtr;        /* Array of options;  delete everything
  1304.                  * referred to recursively by this. */
  1305. {
  1306.     register Element *elPtr;
  1307.     int count;
  1308.  
  1309.     for (count = arrayPtr->numUsed, elPtr = arrayPtr->els;  count > 0;
  1310.         count--, elPtr++) {
  1311.     if (elPtr->flags & NODE) {
  1312.         ClearOptionTree(elPtr->child.arrayPtr);
  1313.     }
  1314.     }
  1315.     ckfree((char *) arrayPtr);
  1316. }
  1317.  
  1318. /*
  1319.  *--------------------------------------------------------------
  1320.  *
  1321.  * GetDefaultOptions --
  1322.  *
  1323.  *    This procedure is invoked to load the default set of options
  1324.  *    for a window.
  1325.  *
  1326.  * Results:
  1327.  *    None.
  1328.  *
  1329.  * Side effects:
  1330.  *    Options are added to those for winPtr's main window.  If
  1331.  *    there exists a RESOURCE_MANAGER proprety for winPtr's
  1332.  *    display, that is used.  Otherwise, the .Xdefaults file in
  1333.  *    the user's home directory is used.
  1334.  *
  1335.  *--------------------------------------------------------------
  1336.  */
  1337.  
  1338. static int
  1339. GetDefaultOptions(interp, winPtr)
  1340.     Tcl_Interp *interp;        /* Interpreter to use for error reporting. */
  1341.     TkWindow *winPtr;        /* Fetch option defaults for main window
  1342.                  * associated with this. */
  1343. {
  1344.     char *regProp;
  1345.     int result, actualFormat;
  1346.     unsigned long numItems, bytesAfter;
  1347.     Atom actualType;
  1348.  
  1349.     /*
  1350.      * Try the RESOURCE_MANAGER property on the root window first.
  1351.      */
  1352.  
  1353.     regProp = NULL;
  1354.     result = XGetWindowProperty(winPtr->display,
  1355.         RootWindow(winPtr->display, 0),
  1356.         XA_RESOURCE_MANAGER, 0, 100000,
  1357.         False, XA_STRING, &actualType, &actualFormat,
  1358.         &numItems, &bytesAfter, (unsigned char **) ®Prop);
  1359.  
  1360.     if ((result == Success) && (actualType == XA_STRING)
  1361.         && (actualFormat == 8)) {
  1362.     result = AddFromString(interp, (Tk_Window) winPtr, regProp,
  1363.         TK_USER_DEFAULT_PRIO);
  1364.     XFree(regProp);
  1365.     return result;
  1366.     }
  1367.  
  1368.     /*
  1369.      * No luck there.  Try a .Xdefaults file in the user's home
  1370.      * directory.
  1371.      */
  1372.  
  1373.     if (regProp != NULL) {
  1374.     XFree(regProp);
  1375.     }
  1376.     result = ReadOptionFile(interp, (Tk_Window) winPtr, "~/.Xdefaults",
  1377.         TK_USER_DEFAULT_PRIO);
  1378.     return result;
  1379. }
  1380.